package server

import (
	"encoding/json"
	"fmt"
	"github.com/go-kratos/kratos/v2/encoding"
	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/transport/http"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	v1 "helloworld/api/helloworld/v1"
	"helloworld/internal/conf"
	"helloworld/internal/service"
	httpNet "net/http"
	"strings"
)

type newErrorDetail struct {
	Code    int32  `json:"code"`
	Reason  string `json:"reason"`
	Message string `json:"message"`
}

type newErrorResponse struct {
	Error newErrorDetail `json:"error"`
}

// NewHTTPServer new an HTTP server.
func NewHTTPServer(c *conf.Server, user *service.UserService, logger log.Logger) *http.Server {
	var opts = []http.ServerOption{
		http.ErrorEncoder(myErrorEncoder),
		http.Middleware(
			recovery.Recovery(),
		),
	}
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}

	opts = append(opts, http.ResponseEncoder(func(w httpNet.ResponseWriter, r *httpNet.Request, i interface{}) error {
		code := httpNet.StatusOK // 默认状态码
		switch r.Method {
		case httpNet.MethodPost:
			code = httpNet.StatusCreated
		case httpNet.MethodDelete:
			code = httpNet.StatusNoContent
		}

		// 设置状态码
		w.WriteHeader(code)
		codec := encoding.GetCodec("json")
		data, err := codec.Marshal(i)
		if err != nil {
			return err
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)
		return nil
	}))

	srv := http.NewServer(opts...)
	v1.RegisterUserHTTPServer(srv, user)
	return srv
}

func myErrorEncoder(w httpNet.ResponseWriter, r *httpNet.Request, err error) {
	se := errors.FromError(err)
	codec, _ := http.CodecForRequest(r, "Accept")
	w.Header().Set("Content-Type", fmt.Sprintf("application/%s", codec.Name()))
	switch se.Code {
	default:
		w.WriteHeader(httpNet.StatusOK)
	case httpNet.StatusUnauthorized, httpNet.StatusForbidden, httpNet.StatusNotFound, httpNet.StatusConflict, httpNet.StatusBadRequest:
		w.WriteHeader(int(se.Code))
	}

	errorDetail := newErrorDetail{
		Code:    se.Code,
		Reason:  se.Reason,
		Message: se.Message,
	}
	//errorDetail.Code = underscoreToUpperCamelCase(errorDetail.Code)
	b, _ := json.Marshal(newErrorResponse{Error: errorDetail})
	_, _ = w.Write(b)
}

func underscoreToUpperCamelCase(s string) string {
	s = strings.Replace(strings.ToLower(s), "_", " ", -1)
	s = cases.Title(language.English).String(s)
	return strings.Replace(s, " ", "", -1)
}
